#include "Renderer.hpp"
#include "../Context/Context.hpp"
#include "../Resource/Shader/ShaderSpecify.hpp"
#include "../Resource/Texture/Texture.hpp"
#include <Utility/Timer.hpp>
#include <common.hpp>
#include <Math/Vector4.hpp>
#include "../Graphics/BMPFont.hpp"
#include "../Graphics/ColorDefine.hpp"
#include <Utility/StringPrintf.hpp>
#include <Utility/CmdParser.hpp>

ZFLAGS_STRING(renderer_clear_color, "0.1 0.1 0.1 1", "Default clear color for zzz::Renderer");
ZFLAGS_DOUBLE(renderer_clear_depth, 1, "Default clear depth for zzz::Renderer");
ZFLAGS_DOUBLE(renderer_camera_near, 0.01, "Near z plane of camera");
ZFLAGS_DOUBLE(renderer_camera_far, 1000, "Far z plane of camera");
ZFLAGS_DOUBLE(renderer_camera_fovy, 60, "Camera Near z plane of camera");
ZFLAGS_BOOL(renderer_show_msg, true, "Show renderer message");
namespace zzz{

Renderer::Renderer()
:showMsg_(ZFLAG_renderer_show_msg),SPF_(0),FPS_(0),context_(NULL),gui_(NULL)
{
  camera_.SetPerspective(width_, height_, ZFLAG_renderer_camera_near, ZFLAG_renderer_camera_far, ZFLAG_renderer_camera_fovy);
}

bool Renderer::InitState()
{
  glShadeModel(GL_SMOOTH);
  GLClearColor::Set(Colorf(FromString<Vector4f>(ZFLAG_renderer_clear_color)));
  GLClearDepth::Set(ZFLAG_renderer_clear_depth);
  glEnable(GL_DEPTH_TEST);
  glDepthFunc(GL_LESS);
  glEnable(GL_BLEND);
  glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
  ColorDefine::white.ApplyGL();

  CHECK_GL_ERROR()
  return true;
}

bool Renderer::InitData()
{
  camera_.Init(this);
  return true;
}

void Renderer::SetupCamera()
{
  camera_.ApplyGL();
}


bool Renderer::RenderScene()
{
  Timer mytimer;
  
  glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
  CHECK_GL_ERROR()
  Draw();
  CHECK_GL_ERROR()

  if (showMsg_)
  {
    CreateMsg();
    RenderText(msg_.c_str());
  }

  SPF_=mytimer.Elapsed();
  FPS_=1.0/SPF_;
  CHECK_GL_ERROR()
  return true;
}

void Renderer::RenderText(const string &msg)
{
  CHECK_GL_ERROR()
  SetRasterPos(0,height_ - BMPFont::Instance().FontHeight() - 1);
  CHECK_GL_ERROR()
  Texture::DisableAll();
  glPixelZoom(1,1);
  BMPFont::Instance().Draw(msg);
  CHECK_GL_ERROR()
}

bool Renderer::Draw()
{
  glMatrixMode(GL_MODELVIEW);
  glPushMatrix();
  glLoadIdentity();

  CHECK_GL_ERROR()
  SetupCamera();
  CHECK_GL_ERROR()
  DrawObj();
  CHECK_GL_ERROR()

  glPopMatrix();
  return true;
}

void Renderer::DrawObj()
{
  glColor3f(1.0,1.0,1.0);
  glBegin(GL_QUADS);
  glVertex3d(-1,-1,0);
  glVertex3d(1,-1,0);
  glVertex3d(1,1,0);
  glVertex3d(-1,1,0);
  glEnd();
}

/////////////////////////////////////////////////////////
// User Event
void Renderer::OnLButtonDown(unsigned int nFlags,int x,int y)
{
}

void Renderer::OnLButtonUp(unsigned int nFlags,int x,int y)
{
}

void Renderer::OnRButtonDown(unsigned int nFlags,int x,int y)
{
  if (camera_.OnRButtonDown(nFlags, x, y))
    Redraw();
}

void Renderer::OnRButtonUp(unsigned int nFlags,int x,int y)
{
  if (camera_.OnRButtonUp(nFlags, x, y))
    Redraw();
}

void Renderer::OnMButtonDown(unsigned int nFlags,int x,int y)
{
  if (camera_.OnMButtonDown(nFlags, x, y))
    Redraw();
}

void Renderer::OnMButtonUp(unsigned int nFlags,int x,int y)
{
  if (camera_.OnMButtonUp(nFlags, x, y))
    Redraw();
}

void Renderer::OnMouseMove(unsigned int nFlags,int x,int y)
{
  if (camera_.OnMouseMove(nFlags, x, y))
    Redraw();
}

void Renderer::OnMouseWheel(unsigned int nFlags, int zDelta, int x,int y)
{
  if (camera_.OnMouseWheel(nFlags, zDelta, x, y))
    Redraw();
}

void Renderer::OnChar(unsigned int nChar, unsigned int nRepCnt, unsigned int nFlags)
{

}

void Renderer::OnKeyDown(unsigned int nChar, unsigned int nRepCnt, unsigned int nFlags)
{

}

void Renderer::OnKeyUp(unsigned int nChar, unsigned int nRepCnt, unsigned int nFlags)
{

}

void Renderer::OnSize(unsigned int nType, int cx, int cy)
{
  if (cy <= 0) return;
  width_ = cx;
  height_ = cy;
  GLViewport::Set(0, 0, width_, height_);
  AfterOnSize(nType, cx, cy);
  glMatrixMode(GL_MODELVIEW);
  Redraw();
}

void Renderer::AfterOnSize(unsigned int nType, int cx, int cy)
{
  camera_.OnSize(nType, cx, cy);
}

Renderer::~Renderer()
{
}

void Renderer::Redraw()
{
  if (context_) context_->Redraw();
}

void Renderer::InstantRedraw()
{
  if (context_) context_->RenderScene();
}

void Renderer::MakeCurrent()
{
  if (context_) context_->MakeCurrent();
}

void Renderer::CreateMsg()
{
  SStringPrintf(msg_,"Res: %d x %d, SPF: %lf = FPS: %lf",width_,height_,SPF_,FPS_);
}

void Renderer::SetContext(Context *context)
{
  context_=context;
}

void Renderer::OnIdle()
{
  return;
}

zzz::Vector3d Renderer::UnProject(double winx, double winy, double winz) const
{
  OpenGLProjector proj;
  return proj.UnProject(winx, winy, winz);
}

void Renderer::SetRasterPos(double winx, double winy) const
{
// it is a trick to set raster pos and make it available even across left or bottom edge
// very useful when need to move image around
// in glBitmap winx winy are relative movement, so need to set to 0 first
  glWindowPos2d(0, 0);
  glBitmap(0, 0, 0, 0, winx, winy, NULL);

  //the following code also works, only that opengl will draw nothing when raster pos is across left or bottom edge
  //you can also set a larger viewport(larger than screen) to make it work,,, ugly hack,,
//  winy = height_ - winy - 1;
//  glWindowPos2d(winx, winy);
}


}
